home *** CD-ROM | disk | FTP | other *** search
/ ...taking it to the Macs! / ...taking it to the Macs!.iso / Extras / ActiveX Mac SDK / ActiveX SDK / Container Common / htparse.c < prev    next >
Text File  |  1997-01-03  |  11KB  |  483 lines

  1.  
  2. /*
  3.    This file was derived from the libwww code, version 2.15, from CERN.
  4.    A number of modifications have been made by Spyglass.
  5.  
  6.    eric@spyglass.com
  7.  */
  8. /*      Parse HyperText Document Address        HTParse.c
  9.    **       ================================
  10.  */
  11. #include "all.h"
  12.  
  13. //jjo 
  14. #ifdef __cplusplus
  15. extern "C" {
  16. #endif
  17.  
  18. char *x_ExpandRelativeAnchor(const char *rel, const char *base);
  19.  
  20. #ifdef __cplusplus
  21. }
  22. #endif
  23.  
  24. #define HEX_ESCAPE '%'
  25.  
  26. struct struct_parts
  27. {
  28.     char *access;
  29.     char *host;
  30.     char *absolute;
  31.     char *relative;
  32. /*  char * search;      no - treated as part of path */
  33.     char *anchor;
  34. };
  35.  
  36.  
  37. /*  Strip white space off a string
  38.    **   ------------------------------
  39.    **
  40.    ** On exit,
  41.    **   Return value points to first non-white character, or to 0 if none.
  42.    **   All trailing white space is OVERWRITTEN with zero.
  43.  */
  44.  
  45. PUBLIC char *HTStrip(char *s)
  46. {
  47. #define SPACE(c) ((c==' ')||(c=='\t')||(c=='\n')||(c=='\r'))
  48.     char *p = s;
  49.     if (!s)
  50.         return NULL;            /* Doesn't dump core if NULL */
  51.     for (p = s; *p; p++) ;        /* Find end of string */
  52.     for (p--; p >= s; p--)
  53.     {
  54.         if (SPACE(*p))
  55.             *p = 0;                /* Zap trailing blanks */
  56.         else
  57.             break;
  58.     }
  59.     while (SPACE(*s))
  60.         s++;                    /* Strip leading blanks */
  61.     return s;
  62. }
  63.  
  64.  
  65. /*  Scan a filename for its consituents
  66.    **   -----------------------------------
  67.    **
  68.    ** On entry,
  69.    **   name    points to a document name which may be incomplete.
  70.    ** On exit,
  71.    **      absolute or relative may be nonzero (but not both).
  72.    **   host, anchor and access may be nonzero if they were specified.
  73.    **   Any which are nonzero point to zero terminated strings.
  74.  */
  75. PRIVATE void scan(char *name, struct struct_parts *parts)
  76. {
  77.     char *after_access;
  78.     char *p;
  79.     int length = strlen(name);
  80.  
  81.     parts->access = 0;
  82.     parts->host = 0;
  83.     parts->absolute = 0;
  84.     parts->relative = 0;
  85.     parts->anchor = 0;
  86.  
  87.     after_access = name;
  88.     for (p = name; *p; p++)
  89.     {
  90.         if (*p == ':')
  91.         {
  92.             *p = 0;
  93.             parts->access = name;    /* Access name has been specified */
  94.             after_access = p + 1;
  95.         }
  96.         if (*p == '/')
  97.             break;
  98.         if (*p == '#')
  99.             break;
  100.     }
  101.  
  102.     if (length > 0)
  103.     {
  104.         for (p = name + length - 1; p >= name; p--)
  105.         {
  106.             if (*p == '#')
  107.             {
  108.                 parts->anchor = p + 1;
  109.                 *p = 0;                /* terminate the rest */
  110.             }
  111.         }
  112.     }
  113.  
  114.     p = after_access;
  115.     if (*p == '/')
  116.     {
  117.         if (p[1] == '/')
  118.         {
  119.             parts->host = p + 2;    /* host has been specified  */
  120.             *p = 0;                    /* Terminate access         */
  121.             p = strchr(parts->host, '/');    /* look for end of host name if any */
  122.             
  123.             // if this is a "file" access, what appears to be a host may really be a volume
  124.             
  125.             if (!strcmp(parts->access, "file"))
  126.             {
  127.                 char                vBuffer[256];
  128.                 
  129.                 // make a proper mac style version of the "host" name
  130.                 {
  131.                     strcpy(vBuffer, parts->host);
  132.                     c2pstr(vBuffer);
  133.                     vBuffer[0] = 1 + p - parts->host;
  134.                     vBuffer[vBuffer[0]] = ':';
  135.                 }
  136.                 
  137.                 // check to see if the "host" name matches that of a mounted volumes
  138.                 
  139.                 if (vBuffer[0] > 1)
  140.                 {
  141.                     HParamBlockRec        vParamBlock;
  142.                     
  143.                     memset(&vParamBlock, 0, sizeof(vParamBlock));
  144.                     vParamBlock.volumeParam.ioNamePtr = (unsigned char *)vBuffer;
  145.                     vParamBlock.volumeParam.ioVolIndex = -1;
  146.                     if (!PBHGetVInfoSync(&vParamBlock))
  147.                     {
  148.                         // it did; we will assume the "host" name is a mistake
  149.                         
  150.                         parts->host = after_access + 1;
  151.                         p = parts->host;
  152.                     }
  153.                 }
  154.             }
  155.             
  156.             if (p)
  157.             {
  158.                 *p = 0;            /* Terminate host */
  159.                 parts->absolute = p + 1;    /* Root has been found */
  160.             }
  161.         }
  162.         else
  163.         {
  164.             parts->absolute = p + 1;    /* Root found but no host */
  165.         }
  166.     }
  167.     else
  168.     {
  169.         parts->relative = (*after_access) ? after_access : 0;    /* zero for "" */
  170.     }
  171.  
  172.     return;
  173. }                                /*scan */
  174.  
  175. /*  Parse a Name relative to another name
  176.    **   -------------------------------------
  177.    **
  178.    **   This returns those parts of a name which are given (and requested)
  179.    **   substituting bits from the related name where necessary.
  180.    **
  181.    ** On entry,
  182.    **   aName       A filename given
  183.    **      relatedName     A name relative to which aName is to be parsed
  184.    **      wanted          A mask for the bits which are wanted.
  185.    **
  186.    ** On exit,
  187.    **   returns     A pointer to a malloc'd string which MUST BE FREED
  188.  */
  189. char *HTParse(const char *aName, const char *relatedName, int wanted)
  190. {
  191.     char *return_value = 0;
  192.     char *p;
  193.     char *access;
  194.     struct struct_parts given, related;
  195.     char name[MAX_URL_STRING+1];
  196.     char rel[MAX_URL_STRING+1];
  197.     char result[2*MAX_URL_STRING+1];    /* Make this longer to avoid overflow */
  198.  
  199.     /* Make working copies of input strings to cut up:
  200.      */
  201.     GTR_strncpy(name, aName, MAX_URL_STRING);
  202.     GTR_strncpy(rel, relatedName, MAX_URL_STRING);
  203.  
  204.     scan(name, &given);
  205.     scan(rel, &related);
  206.  
  207.     /*
  208.         For the given part, if we get a URL which contains a protocol and a host,
  209.         but not an absolute, then it looked something like this:
  210.  
  211.         http://www.spyglass.com:4040
  212.  
  213.         We need to assume that the slash at the end should be there, or when this
  214.         is found as a hyperlink in a document, it will steal the absolute part
  215.         from the URL of the document itself (related).
  216.     */
  217.  
  218.     if (given.access && given.host && !given.absolute)
  219.     {
  220.         GTR_strncpy(name, aName, MAX_URL_STRING);
  221.         strcat(name, "/");
  222.         scan(name, &given);
  223.     }
  224.  
  225.     result[0] = 0;                /* Clear string  */
  226.     access = given.access ? given.access : related.access;
  227.     if (wanted & PARSE_ACCESS)
  228.         if (access)
  229.         {
  230.             strcat(result, access);
  231.             if (wanted & PARSE_PUNCTUATION)
  232.                 strcat(result, ":");
  233.         }
  234.  
  235.     if (given.access && related.access)        /* If different, inherit nothing. */
  236.         if (strcmp(given.access, related.access) != 0)
  237.         {
  238.             related.host = 0;
  239.             related.absolute = 0;
  240.             related.relative = 0;
  241.             related.anchor = 0;
  242.         }
  243.  
  244.     if (wanted & PARSE_HOST)
  245.         if (given.host || related.host)
  246.         {
  247.             char *tail = result + strlen(result);
  248.             if (wanted & PARSE_PUNCTUATION)
  249.                 strcat(result, "//");
  250.             if (given.host)
  251.             {
  252.                 strcat(result, given.host);
  253.             }
  254.             else
  255.             {
  256.                 strcat(result, related.host);
  257.             }
  258.  
  259.             /* Ignore default port numbers, and trailing dots on FQDNs
  260.                which will only cause identical adreesses to look different */
  261.             {
  262.                 char *p;
  263.                 p = strchr(tail, ':');
  264.                 if (p && access)
  265.                 {                /* Port specified */
  266.                     if (   (   strcmp(access, "http") == 0
  267.                             && strcmp(p, ":80") == 0)
  268.                         || (   strcmp(access, "gopher") == 0
  269.                             && strcmp(p, ":70") == 0)
  270. #ifdef SHTTP_ACCESS_TYPE
  271.                         || (   strcmp(access, "shttp") == 0
  272.                             && strcmp(p, ":80") == 0)
  273. #endif
  274.                         )
  275.                         *p = (char) 0;    /* It is the default: ignore it */
  276.                 }
  277.                 if (!p)
  278.                     p = tail + strlen(tail);    /* After hostname */
  279.                 if (strlen (p))    /* -dpg */
  280.                 {
  281.                     p--;            /* End of hostname */
  282.                     if (*p == '.')
  283.                         *p = (char) 0;    /* chop final . */
  284.                 }
  285.             }
  286.         }
  287.  
  288.     if (given.host && related.host)        /* If different hosts, inherit no path. */
  289.         if (strcmp(given.host, related.host) != 0)
  290.         {
  291.             related.absolute = 0;
  292.             related.relative = 0;
  293.             related.anchor = 0;
  294.         }
  295.  
  296.     if (wanted & PARSE_PATH)
  297.     {
  298.         if (given.absolute)
  299.         {                        /* All is given */
  300.             if (wanted & PARSE_PUNCTUATION)
  301.                 strcat(result, "/");
  302.             strcat(result, given.absolute);
  303.         }
  304.         else if (related.absolute)
  305.         {                        /* Adopt path not name */
  306.             strcat(result, "/");
  307.             strcat(result, related.absolute);
  308.             if (given.relative)
  309.             {
  310.                 p = strchr(result, '?');    /* Search part? */
  311.                 if (!p)
  312.                     p = result + strlen(result) - 1;
  313.                 for (; *p != '/'; p--) ;    /* last / */
  314.                 p[1] = 0;        /* Remove filename */
  315.                 strcat(result, given.relative);        /* Add given one */
  316.                 HTSimplify(result);
  317.             }
  318.         }
  319.         else if (given.relative)
  320.         {
  321.             /* The following 3 lines were copied from NCSA Mosaic for Windows */
  322.             if ((wanted & PARSE_HOST) && (given.host || related.host) && (wanted & PARSE_PUNCTUATION))
  323.                 if (result[strlen(result) - 1] != '/')
  324.                     strcat(result, "/");
  325.             strcat(result, given.relative);        /* what we've got */
  326.         }
  327.         else if (related.relative)
  328.         {
  329.             strcat(result, related.relative);
  330.         }
  331.         else
  332.         {                        /* No inheritance */
  333.             if (!strcmp(result, "mailto:"))        // mailto:
  334.                 ;
  335.             else if (!strcmp(result, "news:"))
  336.                 ;
  337.             else // protocol ends with a slash
  338.                 strcat(result, "/");
  339.         }
  340.     }
  341.  
  342.     if (wanted & PARSE_ANCHOR)
  343.         if (given.anchor || related.anchor)
  344.         {
  345.             if (wanted & PARSE_PUNCTUATION)
  346.                 strcat(result, "#");
  347.             strcat(result, given.anchor ? given.anchor : related.anchor);
  348.         }
  349.  
  350.     /* We truncate URLs to 1024 bytes if they're too long. */
  351.     result[MAX_URL_STRING] = '\0';
  352.     return_value = GTR_strdup(result);
  353.  
  354.     return return_value;        /* exactly the right length */
  355. }
  356.  
  357.  
  358. /*
  359.    **   As strcpy() but guaranteed to work correctly
  360.    **   with overlapping parameters.    AL 7 Feb 1994
  361.  */
  362. PRIVATE void ari_strcpy(char *to, char *from)
  363. {
  364.     char *tmp;
  365.  
  366.     if (!to || !from)
  367.         return;
  368.  
  369.     tmp = (char *) GTR_MALLOC(strlen(from) + 1);
  370.     if (tmp)
  371.     {
  372.         strcpy(tmp, from);
  373.         strcpy(to, tmp);
  374.         GTR_FREE(tmp);
  375.     }
  376.     else
  377.     {
  378.         /* TODO */
  379.     }
  380. }
  381.  
  382. /*          Simplify a filename
  383.    //       -------------------
  384.    //
  385.    // A unix-style file is allowed to contain the seqeunce xxx/../ which may be
  386.    // replaced by "" , and the seqeunce "/./" which may be replaced by "/".
  387.    // Simplification helps us recognize duplicate filenames.
  388.    //
  389.    //   Thus,   /etc/junk/../fred   becomes /etc/fred
  390.    //       /etc/junk/./fred    becomes /etc/junk/fred
  391.    //
  392.    //      but we should NOT change
  393.    //       http://fred.xxx.edu/../..
  394.    //
  395.    //   or  ../../albert.html
  396.  */
  397. PUBLIC void HTSimplify(char *filename)
  398. {
  399.     char *p = filename;
  400.     char *q;
  401.  
  402.     if (p)
  403.     {
  404.         while (*p && (*p == '/' || *p == '.'))    /* Pass starting / or .'s */
  405.             p++;
  406.         while (*p)
  407.         {
  408.             if (*p == '/')
  409.             {
  410.                 if ((p[1] == '.') && (p[2] == '.') && (p[3] == '/' || !p[3]))
  411.                 {
  412.                     for (q = p - 1; (q >= filename) && (*q != '/'); q--) ;    /* prev slash */
  413.                     if (q[0] == '/' && 0 != strncmp(q, "/../", 4)
  414.                         && !(q - 1 > filename && q[-1] == '/'))
  415.                     {
  416.                         ari_strcpy(q, p + 3);    /* Remove  /xxx/..  */
  417.                         if (!*filename)
  418.                             strcpy(filename, "/");
  419.                         p = q - 1;    /* Start again with prev slash  */
  420.                     }
  421.                     else
  422.                     {
  423.                         if (q[0] == '/' && (q - 1 > filename && q[-1] == '/'))
  424.                         {
  425.                             /*
  426.                                 The so-called prev slash found is actually the one before the hostname!
  427.  
  428.                                 The URL looks like this:
  429.  
  430.                                 http://host.somewhere.com/../path
  431.                                       ^                     ^
  432.                                       |                     |
  433.                                       q                     p
  434.  
  435.                                 We now need to fix the URL to remove the ../
  436.                             */
  437.                             ari_strcpy(p, p + 3);
  438.                         }
  439.                     }
  440.                 }
  441.                 else if ((p[1] == '.') && (p[2] == '/' || !p[2]))
  442.                 {
  443.                     ari_strcpy(p, p + 2);    /* Remove a slash and a dot */
  444.                 }
  445. #if 0
  446.                 else if (p[-1] != ':')
  447.                 {
  448.                     while (p[1] == '/')
  449.                     {
  450.                         ari_strcpy(p, p + 1);    /* Remove multiple slashes */
  451.                     }
  452.                 }
  453. #endif
  454.             }
  455.             p++;
  456.         }                        /* end while (*p) */
  457.     }                            /* end if (p) */
  458. }
  459.  
  460. /* from html.c */
  461. char *x_ExpandRelativeAnchor(const char *rel, const char *base)
  462. {
  463.     char *pTemp = 0;
  464.     char *stripped;
  465.     char *result = NULL;
  466.  
  467.     if (!rel)
  468.     {
  469.         rel = "";
  470.     }
  471.  
  472.     pTemp = GTR_strdup(rel);
  473.     
  474.     if(!base)
  475.         return pTemp;
  476.         
  477.     stripped = HTStrip(pTemp);
  478.     result = HTParse(stripped, base, PARSE_PUNCTUATION | PARSE_ACCESS | PARSE_HOST | PARSE_PATH | PARSE_ANCHOR);
  479.     GTR_FREE(pTemp);
  480.     return result;
  481. }
  482.  
  483.